package cn.com.flower.passport.service.impl;

import cn.com.flower.common.enumerator.ServiceCode;
import cn.com.flower.common.ex.ServiceException;
import cn.com.flower.common.pojo.authentication.CurrentPrincipal;
import cn.com.flower.common.pojo.po.UserCachePO;
import cn.com.flower.common.util.JwtUtils;
import cn.com.flower.passport.dao.cache.IUserCacheRepository;
import cn.com.flower.passport.dao.persist.repository.IUserRepository;
import cn.com.flower.passport.pojo.param.UserLoginInfoParam;
import cn.com.flower.passport.pojo.vo.UserLoginInfoVO;
import cn.com.flower.passport.pojo.vo.UserLoginResultVO;
import cn.com.flower.passport.service.IUserService;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class UserService implements IUserService {

    @Value("${flower.jwt.secret-key}")
    private String secretKey;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private IUserRepository iUserRepository;
    @Autowired
    private IUserCacheRepository iUserCacheRepository;

    /**
     * 用户登录
     * @param userLoginInfoParam 用户登录信息
     * @return
     */
    @Override
    public UserLoginResultVO login( UserLoginInfoParam userLoginInfoParam, String remoteAddr, String userAgent) {
        UserLoginInfoVO userLogin = iUserRepository.getLoginInfoByUsername(userLoginInfoParam.getUsername());
        // 判断用户是否存在
        if (userLogin == null) {
            String massage = "登录失败,用户名或密码错误!";
            throw new ServiceException(ServiceCode.ERROR_NOT_FOUND, massage);
        }
        // 判断是否启用，不是1抛异常
        if (userLogin.getEnable() != 1) {
            String massage = "登录失败,账户已被禁用!";
            throw new ServiceException(ServiceCode.ERROR_UNAUTHORIZED_DISABLED, massage);
        }
        // 从参数中获取密码
        String password = userLoginInfoParam.getPassword();
        // 判断查询结果中的密码，与以上参数密码，是否不匹配
        if (!passwordEncoder.matches(password, userLogin.getPassword())) {// 用加密密码
            String massage = "登录失败,用户名或密码错误!";
            throw new ServiceException(ServiceCode.ERROR_UNAUTHORIZED, massage);
        }
        // 登陆成功,准备返回的结果数据
        UserLoginResultVO userLoginResult = new UserLoginResultVO();
        BeanUtils.copyProperties(userLogin, userLoginResult);

        // 生成当前用户token
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", userLogin.getId());
        claims.put("username", userLogin.getUsername());
        claims.put("password", userLogin.getPassword());
        claims.put("remoteAddr", remoteAddr);
        claims.put("userAgent", userAgent);
        String jwt = Jwts.builder()
                // header
                .setHeaderParam("alg", "HS256")
                .setHeaderParam("typ", "JWT")
                // payload
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                // done
                .compact();
        userLoginResult.setToken(jwt);

        // 将用户的相关数据写入到Redis中
        List<String> authorities = userLoginResult.getAuthorities();// 获取用户权限列表
        String jsonString = JSON.toJSONString(authorities);// 将权限列表的字符串转换成JSONArray
        UserCachePO userCachePO = new UserCachePO();// 创建用户权限对象
        userCachePO.setAuthoritiesJsonString(jsonString);// 设置用户状态对象
        userCachePO.setEnable(1);// 设置用户启用状态
        iUserCacheRepository.saveUser(userLoginResult.getId(), userCachePO);
        return userLoginResult;
    }

    /**
     * 退出登录
     * @param currentPrincipal
     */
    @Override
    public void logout(CurrentPrincipal currentPrincipal) {
        iUserCacheRepository.deleteUserState(currentPrincipal.getId());
    }
}
